package dominio.fantasma.estrategiasMovimiento;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;

import dominio.comun.Posicion;
import dominio.comun.sentidosMovimiento.SentidoFactory;
import dominio.comun.sentidosMovimiento.SentidoMovimiento;
import dominio.fantasma.IFantasma;
import dominio.laberinto.Casillero;
import dominio.laberinto.ICasillero;
import dominio.laberinto.Laberinto;



/**
 * @author RJC
 * @version 1.0
 * @created 16-jun-2012 11:59:27
 */
public abstract class EstrategiaMovimiento {

	protected int VISION_STANDARD = 2;
		/**
	 * 
	 * @param unContexto
	 * @param meAcerco
	 */
	
	/*template method de como me muevo*/
	public Posicion aDondeMeMuevo(IFantasma unContexto, boolean meAcerco){
		
		Posicion posicionFantasma = unContexto.getPosicion();
		ICasillero casilleroActual = Laberinto.getInstance().searchCasillero(posicionFantasma);
		boolean estadoBifurcacion = this.definirBifurcacion (casilleroActual);
		
		
		if (!estadoBifurcacion){
			return this.seguirMoviendoConMismoSentido(unContexto);			
		}		
		else{
			return this.queHacerAnteUnaBifurcacion(unContexto, meAcerco);
		}
		
	}
	
	private boolean definirBifurcacion (ICasillero casilleroActual){
		int posibilidadMovimientos = 4;
		if (casilleroActual.getCasilleroAbajo() == null){posibilidadMovimientos= posibilidadMovimientos -1;}
		if (casilleroActual.getCasilleroArriba() == null){posibilidadMovimientos= posibilidadMovimientos -1;}
		if (casilleroActual.getCasilleroDerecha() == null){posibilidadMovimientos= posibilidadMovimientos -1;}
		if (casilleroActual.getCasilleroIzquierda() == null){posibilidadMovimientos= posibilidadMovimientos -1;}
		
		//si me puedo mover a mas de 3 lugares entonces estoy en presencia de una bifuracacion
		return posibilidadMovimientos >= 3;
		
	}
	
	private boolean sePuedeDoblarADerecha(SentidoMovimiento sentidoActual, Posicion posActual){
		SentidoMovimiento nuevoSentido;
		nuevoSentido = sentidoActual.getSentidoADerecha();
		return nuevoSentido.getSiguientePosicion(posActual) != null;
		
	}
	
	private boolean sePuedeDoblarAIzquierda(SentidoMovimiento sentidoActual, Posicion posActual){
		SentidoMovimiento nuevoSentido;
		nuevoSentido = sentidoActual.getSentidoAIzquierda();
		return nuevoSentido.getSiguientePosicion(posActual) != null;
	}
	
	private Posicion seguirMoviendoConMismoSentido(IFantasma unContexto){
		
		Posicion posicionFantasma = unContexto.getPosicion();
		Posicion posicionAMover;
		
		//caso comun (todos se mueven igual)
		//intento moverme con la misma direccion que tenia
		posicionAMover = unContexto.getSentidoMovimiento().getSiguientePosicion(posicionFantasma);
		
		if (posicionAMover == null){
			//tengo que cambiar de sentido de movimiento (no puedo seguir en esta direccion)
			SentidoMovimiento nuevoSentido = null;
			boolean sePuedeDoblar = false;
			
			//trato de doblar a derecha				
			if (this.sePuedeDoblarADerecha(unContexto.getSentidoMovimiento(), posicionFantasma) ) {
				nuevoSentido = unContexto.getSentidoMovimiento().getSentidoADerecha();
				sePuedeDoblar = true;
			} 
			
			//trato de doblar a izquierda				
			if (this.sePuedeDoblarAIzquierda(unContexto.getSentidoMovimiento(), posicionFantasma)) {
				nuevoSentido = unContexto.getSentidoMovimiento().getSentidoAIzquierda();
				sePuedeDoblar = true;
			} 
			
			if (!sePuedeDoblar){
			//trato de volver sobre mis pasos (siempre se puede)
				nuevoSentido = unContexto.getSentidoMovimiento().getSentidoAOpuesto();
			}
			
			//actualizo el sentido de movimiento y me muevo
			unContexto.setSentidoMovimiento(nuevoSentido);
			posicionAMover = nuevoSentido.getSiguientePosicion(posicionFantasma);
			
			
		}
		return posicionAMover;
		
	}
		
	protected boolean elPacmanEstaEnRangoVision(IFantasma unContexto){
		Posicion posPacman = Laberinto.getInstance().getPacman().getPosicion();
		Posicion posFantasma = unContexto.getPosicion();
		
		double distancia = this.getDistancia(posPacman, posFantasma); 
		
		return (distancia < getRangoVision(unContexto));		
	}
	
	private double getDistancia (Posicion posPacman, Posicion posFantasma){
		int DiferenciaX = Math.abs(posPacman.getPosX() - posFantasma.getPosX());
		int DiferenciaY = Math.abs(posPacman.getPosY() - posFantasma.getPosY());
		
		return Math.sqrt( Math.pow(DiferenciaX, 2) + Math.pow(DiferenciaY, 2) );
		
	}

	protected Posicion moverseSegunElPacman(IFantasma unContexto, boolean meAcerco, Posicion posPacman){
		Posicion posFantasma = unContexto.getPosicion();
		
		SentidoMovimiento MaximizaDistancia = null;
		SentidoMovimiento MinimizaDistancia = null;
		double distanciaMaxima = 0;
		double distanciaMinima = Double.POSITIVE_INFINITY;

		Collection<SentidoMovimiento> sentidosPosibles = this.getSentidosPosibles(unContexto);
		
		Iterator<SentidoMovimiento> itSentidos = sentidosPosibles.iterator();
		SentidoMovimiento unSentido;
		double unaDistancia;
		
		
		while (itSentidos.hasNext() ){
			unSentido = itSentidos.next();
			unaDistancia = getDistancia(posPacman, unSentido.getSiguientePosicion(posFantasma)); 
			if ( unaDistancia > distanciaMaxima ){
				distanciaMaxima = unaDistancia;
				MaximizaDistancia = unSentido;
			}
			if ( unaDistancia < distanciaMinima){
				distanciaMinima = unaDistancia;
				MinimizaDistancia = unSentido;
			}	
		}
		
		if ( meAcerco){//busco mimizar la distancia al pacman
			unContexto.setSentidoMovimiento(MinimizaDistancia);
			return MinimizaDistancia.getSiguientePosicion(posFantasma);			
		}
		else{//busco maximizar la distancia al pacman
			unContexto.setSentidoMovimiento(MaximizaDistancia);
			return MaximizaDistancia.getSiguientePosicion(posFantasma);
			
		}
				
	}

	private Collection<SentidoMovimiento> getSentidosPosibles(IFantasma unContexto){
		Posicion posFantasma = unContexto.getPosicion();
		Collection<SentidoMovimiento> posibles = new ArrayList<SentidoMovimiento>();
		
		if (SentidoFactory.getSentidoAbajo().getSiguientePosicion(posFantasma) != null){
			posibles.add(SentidoFactory.getSentidoAbajo());}
		if (SentidoFactory.getSentidoArriba().getSiguientePosicion(posFantasma) != null){
			posibles.add(SentidoFactory.getSentidoArriba());}
		if (SentidoFactory.getSentidoIzquierda().getSiguientePosicion(posFantasma) != null){
			posibles.add(SentidoFactory.getSentidoIzquierda());}
		if (SentidoFactory.getSentidoDerecha().getSiguientePosicion(posFantasma) != null){
			posibles.add(SentidoFactory.getSentidoDerecha());}
		
		return posibles;	
		
		
	}
	
	protected Posicion moverseAleatoriamente(IFantasma unContexto){
		int randomNumber; 
		boolean decisionPosible = false;
		
		while (!decisionPosible){
			randomNumber = new Random().nextInt(4);
			
			switch (randomNumber){
			case 0:
				unContexto.setSentidoMovimiento(SentidoFactory.getSentidoAbajo());
				break;
			case 1:
				unContexto.setSentidoMovimiento(SentidoFactory.getSentidoArriba());
				break;
			case 2:
				unContexto.setSentidoMovimiento(SentidoFactory.getSentidoIzquierda());
				break;
			case 3:
				unContexto.setSentidoMovimiento(SentidoFactory.getSentidoDerecha());
				break;
			}
			
			if (unContexto.getSentidoMovimiento().getSiguientePosicion(unContexto.getPosicion())!= null){
				decisionPosible = true;				
			}
		}
		return unContexto.getSentidoMovimiento().getSiguientePosicion(unContexto.getPosicion());
		
	}

	protected abstract void alFinalizar(IFantasma unContexto);

	protected abstract int getRangoVision(IFantasma unContexto);

	protected abstract Posicion queHacerAnteUnaBifurcacion(IFantasma unContexto, boolean meAcerco);
	
}